package concurrent;

import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * runAsync方法不支持返回值。
 * supplyAsync可以支持返回值。
 *
 * @author jack.wu
 * @since 2019/12/24 15:02
 */
public class CompletableFutureDemo {

    /**
     * 无返回值
     *
     * @throws Exception
     */
    public static void runSync() throws Exception {
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() +" run end ...");
        });
        Void aVoid = future.get();
        System.out.println(aVoid);
    }

    /**
     * 有返回值
     *
     * @throws Exception
     */
    public static void supplySync() throws Exception {
        CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("run end ...");
            return System.currentTimeMillis();
        });
        Long time = future.get();
        System.out.println("time = " + time);
    }

    /**
     * whenComplete：是执行当前任务的线程执行继续执行 whenComplete 的任务。
     * whenCompleteAsync：是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。
     *
     * @throws InterruptedException
     */
    public static void whenComplete() throws InterruptedException {
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (new Random().nextInt() % 2 != 0) {
                int i = 3 / 0;
            }
            System.out.println("run end ...");
        });

        future.whenComplete((aVoid, throwable) -> System.out.println("执行完成"));

        future.exceptionally(throwable -> {
            System.out.println("执行失败" + throwable.getMessage());
            return null;
        });

        TimeUnit.SECONDS.sleep(2);
    }

    /**
     * 当一个线程依赖另一个线程时，可以使用 thenApply 方法来把这两个线程串行化
     * thenApply 只可以执行正常的任务，任务出现异常则不执行 thenApply 方法。
     *
     * @throws Exception
     */
    public static void thenApply() throws Exception {
        CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> {
            long result = new Random().nextInt(100);
            System.out.println("result = " + result);
            return result;
        }).thenApply(aLong -> {
            long result1 = aLong * 5;
            System.out.println("result1 = " + result1);
            return result1;
        });
        Long result = future.get();
        System.out.println(result);
    }

    /**
     * handle 方法和 thenApply 方法处理方式基本一样。不同的是 handle 是在任务完成后再执行，还可以处理异常的任务。
     * thenApply 只可以执行正常的任务，任务出现异常则不执行 thenApply 方法。
     */
    public static void handle() throws Exception {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 0;
            return new Random().nextInt(10);
        }).handle((o, throwable) -> {
            int result = -1;
            if (throwable == null) {
                result = o * 2;
            } else {
                System.out.println(throwable.getMessage());
            }
            return result;
        });
        System.out.println(future.get());
    }

    /**
     * 接收任务的处理结果，并消费处理，无返回结果。
     *
     * @throws Exception
     */
    public static void thenAccept() throws Exception {
        CompletableFuture<Void> future = CompletableFuture.supplyAsync(() ->
                new Random().nextInt(10))
                .thenAccept(System.out::println);
        Void aVoid = future.get();
    }

    /**
     * 跟 thenAccept 方法不一样的是，不关心任务的处理结果。只要上面的任务执行完成，就开始执行 thenAccept
     */
    public static void thenRun() {
        CompletableFuture<Void> future = CompletableFuture.supplyAsync(() ->
                new Random().nextInt(10)).thenRun(() -> {
            System.out.println("thenRun ...");
        });
    }

    /**
     * thenCombine 会把 两个 CompletionStage 的任务都执行完成后，把两个任务的结果一块交给 thenCombine 来处理
     */
    public static void thenCombine() throws Exception {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "hello");

        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "wwj");

        CompletableFuture<String> result = future1.thenCombine(future2, (s, s2) -> s + s2);
        System.out.println(result.get());
    }

    /**
     * 当两个CompletionStage都执行完成后，把结果一块交给thenAcceptBoth来进行消耗
     * runAfterBoth 相同
     */
    public static void thenAcceptBoth() {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            int i = new Random().nextInt(3);
            try {
                TimeUnit.SECONDS.sleep(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("i = " + i);
            return i;
        });

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            int i1 = new Random().nextInt(3);
            try {
                TimeUnit.SECONDS.sleep(i1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("i1 = " + i1);
            return i1;
        });

        future.thenAcceptBoth(future1, (integer, integer2) -> {
            System.out.println("future1 :" + integer + "---future2 :" + integer2);
        });

    }

    /**
     * 两个CompletionStage，谁执行返回的结果快，我就用那个CompletionStage的结果进行下一步的转化操作。
     * acceptEither  相同
     * runAfterEither 任何一个完成了都会执行下一步的操作（Runnable）
     */
    public static void applyToEither() {
        CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> {
            int i = new Random().nextInt(3);
            try {
                TimeUnit.SECONDS.sleep(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("i = " + i);
            return i;
        });

        CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> {
            int i1 = new Random().nextInt(3);
            try {
                TimeUnit.SECONDS.sleep(i1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("i = " + i1);
            return i1;
        });

        f1.applyToEither(f2, integer -> {
            System.out.println(integer);
            return integer * 2;
        });
    }

    /**
     * thenCompose 方法允许你对两个 CompletionStage 进行流水线操作，
     * 第一个操作完成时，将其结果作为参数传递给第二个操作。
     *
     * @throws Exception
     */
    public static void thenCompose() throws Exception {
        CompletableFuture<Integer> result = CompletableFuture.supplyAsync(() -> {
            int i = new Random().nextInt(10);
            System.out.println("i = " + i);
            return i;
        }).thenCompose(integer -> CompletableFuture.supplyAsync(() -> integer * 2));
        System.out.println(result.get());
    }

    public static void main(String[] args) throws Exception {
//        runSync();
//        supplySync();
//        whenComplete();
//        thenApply();
//        handle();
//        thenAccept();
//        thenRun();
//        thenCombine();
//        thenAcceptBoth();
//        applyToEither();
//        thenCompose();
        System.out.println(Thread.currentThread().getName()+"  执行完成！！");
    }

}
